package org.meb.lotrlcg.scan;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.meb.lotrlcg.scan.model.Card;
import org.meb.lotrlcg.scan.model.Card.Type;
import org.meb.lotrlcg.scan.model.InfoPair;
import org.meb.lotrlcg.scan.model.InfoProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SiteParser {

	private static final Logger log = LoggerFactory.getLogger(SiteParser.class);

	private static final Map<String, InfoProperty> commonInfosMap;
	private static final Map<String, InfoProperty> allyInfosMap;
	private static final Map<String, InfoProperty> attachmentInfosMap;
	private static final Map<String, InfoProperty> enemyInfosMap;
	private static final Map<String, InfoProperty> locationInfosMap;
	private static final Map<String, InfoProperty> questInfosMap;
	private static final Map<String, InfoProperty> treacheryInfosMap;

	static {
		commonInfosMap = new HashMap<String, InfoProperty>();
		commonInfosMap.put("set:", new InfoProperty("set"));
		commonInfosMap.put("number:", new InfoProperty("number"));
		commonInfosMap.put("quantity:", new InfoProperty("quantity"));
		commonInfosMap.put("illustrator:", new InfoProperty("illustrator"));

		allyInfosMap = new HashMap<String, InfoProperty>();
		allyInfosMap.put("sphere:", new InfoProperty("sphere"));
		allyInfosMap.put("cost:", new InfoProperty("cost"));
		allyInfosMap.put("willpower:", new InfoProperty("willpower"));
		allyInfosMap.put("attack:", new InfoProperty("attack"));
		allyInfosMap.put("defense:", new InfoProperty("defense"));
		allyInfosMap.put("hit points:", new InfoProperty("hitPoints"));
		
		attachmentInfosMap = new HashMap<String, InfoProperty>();
		attachmentInfosMap.put("sphere:", new InfoProperty("sphere"));
		attachmentInfosMap.put("cost:", new InfoProperty("cost"));

		enemyInfosMap = new HashMap<String, InfoProperty>();
		enemyInfosMap.put("encounter set:", new InfoProperty("encounterSet"));
		enemyInfosMap.put("threat threashold:", new InfoProperty("engageThreat"));
		enemyInfosMap.put("threat:", new InfoProperty("threat"));
		enemyInfosMap.put("attack:", new InfoProperty("attack"));
		enemyInfosMap.put("defense:", new InfoProperty("defense"));
		enemyInfosMap.put("hit points:", new InfoProperty("hitPoints"));

		locationInfosMap = new HashMap<String, InfoProperty>();
		locationInfosMap.put("encounter set:", new InfoProperty("encounterSet"));
		locationInfosMap.put("threat:", new InfoProperty("threat"));
		// locationInfosMap.put("quest points", new
		// InfoProperty("questPoints"));
		
		questInfosMap = new HashMap<String, InfoProperty>();
		questInfosMap.put("encounter set:", new InfoProperty("encounterSet"));
		questInfosMap.put("encounter info:", new InfoProperty("encounterInfo"));
		// questInfosMap.put("quest points", new InfoProperty("questPoints"));
		
		treacheryInfosMap = new HashMap<String, InfoProperty>();
		treacheryInfosMap.put("encounter set:", new InfoProperty("encounterSet"));
		
	}

	public static void main(String[] args) throws IOException {
		String url = "http://www.cardgamedb.com/index.php/lotr/lord-of-the-rings-card-spoiler/_/khazad-dum/";
		new SiteParser().parseSite(url, System.out);
	}

	public void parseSite(String url, OutputStream outStream) throws IOException {
		Document siteDoc = Jsoup.connect(url).timeout(20000).get();
		Elements aElems = siteDoc.select("div.topic_controls li.page a");
		Iterator<Element> iter = aElems.iterator();
		while (iter.hasNext()) {
			Element aElem = iter.next();
			System.out.println(aElem.attr("href"));
		}

		parseCards(siteDoc, outStream);
	}

	private List<Card> parseCards(Document siteDoc, OutputStream outStream) {
		List<Card> cards = new ArrayList<Card>();

		Elements tdCards = siteDoc.select("div.ipsBox table.ipb_table > tbody > tr > td");
		Iterator<Element> iter = tdCards.iterator();
		while (iter.hasNext()) {
			Element tdCard = iter.next();
			Element tdCardInfo = tdCard.select("tr:eq(0) > td").get(1);
			Card card = parseCard(tdCardInfo);
			if (card != null) {
				cards.add(card);
			}
		}

		return cards;
	}

	private Card parseCard(Element tdCardInfo) {
		String name = tdCardInfo.child(0).text().trim();
		String typeString = tdCardInfo.child(2).nextSibling().toString().trim();
		Type type = Card.Type.valueOf(typeString.toUpperCase());

		log.info("parseCard(): type: {}, name: {}", type, name);

		Card card = new Card(type);
		switch (type) {
			case ALLY:
				parseAllyCardInfo(card, tdCardInfo);
				break;
			case ATTACHMENT:
				parseAttachmentCardInfo(card, tdCardInfo);
				break;
			case ENEMY:
				parseEnemyCardInfo(card, tdCardInfo);
				break;
			case EVENT:
				parseEventCardInfo(card, tdCardInfo);
				break;
			case HERO:
				parseHeroCardInfo(card, tdCardInfo);
				break;
			case LOCATION:
				parseLocationCardInfo(card, tdCardInfo);
				break;
			case OBJECTIVE:
				parseObjectiveCardInfo(card, tdCardInfo);
				break;
			case QUEST:
				parseQuestCardInfo(card, tdCardInfo);
				break;
			case TREACHERY:
				parseTreacheryCardInfo(card, tdCardInfo);
				break;
			default:
				card = null;
		}

		log.info("card: {}", card);

		return card;
	}

	private void parseCommonCardInfo(Card card, Element tdCardInfo) {
		card.setName(tdCardInfo.child(0).text().trim());
		List<InfoPair> infoPairs = extractInfoPairs(tdCardInfo, commonInfosMap.keySet());
		setCardInfoPairValues(card, infoPairs, commonInfosMap);
	}

	private void parseAllyCardInfo(Card card, Element tdCardInfo) {
		parseCommonCardInfo(card, tdCardInfo);
		List<InfoPair> infoPairs = extractInfoPairs(tdCardInfo, allyInfosMap.keySet());
		setCardInfoPairValues(card, infoPairs, allyInfosMap);
	}

	private void parseAttachmentCardInfo(Card card, Element tdCardInfo) {
		parseCommonCardInfo(card, tdCardInfo);
		List<InfoPair> infoPairs = extractInfoPairs(tdCardInfo, attachmentInfosMap.keySet());
		setCardInfoPairValues(card, infoPairs, attachmentInfosMap);
	}

	private void parseEnemyCardInfo(Card card, Element tdCardInfo) {
		parseCommonCardInfo(card, tdCardInfo);
		List<InfoPair> infoPairs = extractInfoPairs(tdCardInfo, enemyInfosMap.keySet());
		setCardInfoPairValues(card, infoPairs, enemyInfosMap);
	}

	private void parseEventCardInfo(Card card, Element tdCardInfo) {
		
	}

	private void parseHeroCardInfo(Card card, Element tdCardInfo) {
		
	}

	private void parseLocationCardInfo(Card card, Element tdCardInfo) {
		parseCommonCardInfo(card, tdCardInfo);
		List<InfoPair> infoPairs = extractInfoPairs(tdCardInfo, locationInfosMap.keySet());
		setCardInfoPairValues(card, infoPairs, locationInfosMap);
	}

	private void parseObjectiveCardInfo(Card card, Element tdCardInfo) {
		// TODO Auto-generated method stub
		
	}

	private void parseQuestCardInfo(Card card, Element tdCardInfo) {
		parseCommonCardInfo(card, tdCardInfo);
	
		// Iterator<InfoPair> iter = extracteInfoPairs(tdCardInfo).iterator();
		// while (iter.hasNext()) {
		// InfoPair infoPair = iter.next();
		// String infoName = infoPair.getName();
		// Object infoValue = infoPair.getValue();
		// if (infoName.equalsIgnoreCase("encounter set:")) {
		// card.setEncounterSet(infoValue.toString());
		// } else if (infoName.equalsIgnoreCase("encounter info:")) {
		// card.setEncounterInfo(infoValue.toString());
		// } else if (infoName.equalsIgnoreCase("quest points:")) {
		// card.setQuestPoints(Integer.parseInt(infoValue.toString()));
		// }
		// }
		List<InfoPair> infoPairs = extractInfoPairs(tdCardInfo, questInfosMap.keySet());
		setCardInfoPairValues(card, infoPairs, questInfosMap);
	}

	private void parseTreacheryCardInfo(Card card, Element tdCardInfo) {
		parseCommonCardInfo(card, tdCardInfo);
		// Iterator<InfoPair> iter = extracteInfoPairs(tdCardInfo).iterator();
		// while (iter.hasNext()) {
		// InfoPair infoPair = iter.next();
		// String infoName = infoPair.getName();
		// Object infoValue = infoPair.getValue();
		// if (infoName.equalsIgnoreCase("encounter set:")) {
		// card.setEncounterSet(infoValue.toString());
		// }
		// }
		List<InfoPair> infoPairs = extractInfoPairs(tdCardInfo, treacheryInfosMap.keySet());
		setCardInfoPairValues(card, infoPairs, treacheryInfosMap);
	}

	

	// private List<InfoPair> extracteInfoPairs(Element tdCardInfo) {
	// HashSet<String> validInfoNames = new HashSet<String>();
	// validInfoNames.add("set:");
	// validInfoNames.add("number:");
	// validInfoNames.add("quantity:");
	// validInfoNames.add("illustrator:");
	// validInfoNames.add("encounter set:");
	// validInfoNames.add("encounter info:");
	// validInfoNames.add("quest points:");
	// validInfoNames.add("threat threashold:");
	// validInfoNames.add("threat:");
	// validInfoNames.add("attack:");
	// validInfoNames.add("defense:");
	// validInfoNames.add("hit points:");
	// return extractInfoPairs(tdCardInfo, validInfoNames);
	// }

	private List<InfoPair> extractInfoPairs(Element tdCardInfo, Set<String> infoNames) {
		ArrayList<InfoPair> infoPairs = new ArrayList<InfoPair>();
		Elements bInfoNames = tdCardInfo.select("> b");
		Iterator<Element> iter = bInfoNames.iterator();
		while (iter.hasNext()) {
			Element bInfoName = iter.next();
			String infoName = bInfoName.text();
			if (StringUtils.isNotBlank(infoName) && infoNames.contains(infoName.toLowerCase())) {
				String infoValue = bInfoName.nextSibling().toString().trim();
				if (infoValue != null) {
					infoPairs.add(new InfoPair(infoName, infoValue));
					log.info("extracteInfoPairs(): infoName: {}, infoValue: {}", infoName,
							infoValue);
				}
			}
		}
		return infoPairs;
	}

	private void setCardInfoPairValues(Card card, List<InfoPair> infoPairs,
			Map<String, InfoProperty> infosMap) {
		Iterator<InfoPair> iter = infoPairs.iterator();
		while (iter.hasNext()) {
			InfoPair infoPair = iter.next();
			String infoName = infoPair.getName();
			Object infoValue = infoPair.getValue();
			if (infosMap.keySet().contains(infoName.toLowerCase())) {
				try {
					String propertyName = infosMap.get(infoName.toLowerCase()).getPropertyName();
					log.debug("setCardInfoPairValues(): set property: {} <- {}", propertyName,
							infoValue);
					BeanUtils.setProperty(card, propertyName, infoValue);

					Object value = PropertyUtils.getProperty(card, propertyName);
					log.debug("setCardInfoPairValues(): get property: {} -> {}", propertyName,
							value);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}

	// private String extractTraits(Element tdCardInfo) {
	// Elements bInfoNames = tdCardInfo.select("> b");
	// Iterator<Element> iter = bInfoNames.iterator();
	// while (iter.hasNext()) {
	// Element bInfoName = iter.next();
	// Node sibling = bInfoName.nextSibling();
	// if (sibling == null || sibling.)
	// }
	// }
}
